#include "goahead.h"
#include "common/cJSON.h"
#include <stdint.h>
#include <memory.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>

#define HOME "/opt/Tim/www"
#define ROUTE "/opt/Tim/routes"
#define LOG_PATH "/opt/Tim/www/log.txt"

static uint8_t REMOTE_SERVER_IP[32] = {0};
static uint32_t REMOTE_SERVER_PORT = 0;
// static uint8_t qx_user[64];
// static uint8_t qx_password[64];
// static uint8_t qx_service_name[128];
// static uint32_t qx_port;

static bool IS_SAVE = false;

static void writeCORSHeader(Webs *wp)
{
    websWriteHeaders(wp, -1, 0);
    websWriteHeader(wp, "Access-Control-Allow-Origin", "*");
    websWriteHeader(wp, "Content-Type", "application/json");
    websWriteHeader(wp, "Access-Control-Allow-Private-Network", "true");
    websWriteHeader(wp, "Access-Control-Allow-Method", "GET, OPTIONS, POST");
    websWriteEndHeaders(wp);
}

bool mkdirCommon(uint8_t *path)
{
    if (access(path, F_OK) != 0)
    {
        if (mkdir(path, S_IRWXU) != 0)
        {
            return false;
        }
    }
    return true;
}

static bool optionsHandler(Webs *wp)
{
    if (smatch(wp->method, "OPTIONS"))
    {
        websSetStatus(wp, HTTP_CODE_OK);
        websWriteHeaders(wp, -1, 0);
        websWriteHeader(wp, "Allow", "GET,OPTIONS,POST");
        websWriteHeader(wp, "Access-Control-Allow-Origin", "*");
        websWriteHeader(wp, "Access-Control-Allow-Headers", "Content-Type");
        websWriteHeader(wp, "Content-Type", "application/json");
        websWriteHeader(wp, "Access-Control-Allow-Private-Network", "true");
        websWriteHeader(wp, "Access-Control-Allow-Method", "GET, OPTIONS, POST");
        websWriteEndHeaders(wp);
        websDone(wp);
        return true;
    }
    websResponse(wp, HTTP_CODE_NOT_ACCEPTABLE, "Unsupported method");
    return false;
}

PUBLIC int websOptionsOpen(void)
{
    websDefineHandler("options", 0, optionsHandler, 0, 0);
    return 0;
}

static void getJson(Webs *wp)
{
    websSetStatus(wp, HTTP_CODE_OK);
    writeCORSHeader(wp);

    FILE *f = fopen("/opt/Tim/remote_cfg.json", "r");

    if (f == NULL)
    {
        websSetStatus(wp, HTTP_CODE_INTERNAL_SERVER_ERROR);

        websFlush(wp, 0);
        websDone(wp);
        return;
    }

    char buf[10240] = {0};
    fread(buf, 1, sizeof(buf), f);
    fclose(f);

    cJSON *root = cJSON_Parse(buf);

    if (root != NULL)
    {
        cJSON *json_item_CenterServer = cJSON_GetObjectItem(root, "CenterServer");
        cJSON *json_laneroad = cJSON_GetObjectItem(root, "LaneRoad");
        cJSON *json_array = cJSON_GetArrayItem(json_laneroad, 0);
        if (IS_SAVE == false)
        {
            cJSON *json_item_remote_ip = cJSON_GetObjectItem(json_item_CenterServer, "DataMasterServerIp");
            cJSON *json_item_remote_port = cJSON_GetObjectItem(json_item_CenterServer, "DataMasterServerPort");
            // cJSON *json_item_qx_user = cJSON_GetObjectItem(json_array, "QxUser");
            // cJSON *json_item_qx_password = cJSON_GetObjectItem(json_array, "QxPassword");
            // cJSON *json_item_service_name = cJSON_GetObjectItem(json_array, "QxServiceName");
            // cJSON *json_item_qx_port = cJSON_GetObjectItem(json_array, "QxPort");

            if (
                json_item_remote_ip != NULL &&
                json_item_remote_port != NULL
                // json_item_qx_user != NULL &&
                // json_item_qx_password != NULL &&
                // json_item_service_name != NULL &&
                // json_item_qx_port != NULL
            )
            {
                memcpy(REMOTE_SERVER_IP, json_item_remote_ip->valuestring, MIN(sizeof(REMOTE_SERVER_IP), strlen(json_item_remote_ip->valuestring)));
                REMOTE_SERVER_PORT = json_item_remote_port->valueint;

                // memcpy(qx_user, json_item_qx_user->valuestring, MIN(sizeof(qx_user), strlen(json_item_qx_user->valuestring)));
                // memcpy(qx_password, json_item_qx_password->valuestring, MIN(sizeof(qx_password), strlen(json_item_qx_password->valuestring)));
                // memcpy(qx_service_name, json_item_service_name->valuestring, MIN(sizeof(qx_service_name), strlen(json_item_service_name->valuestring)));
                // qx_port = json_item_qx_port->valueint;
            }
            else
            {
                websSetStatus(wp, HTTP_CODE_INTERNAL_SERVER_ERROR);
            }
            IS_SAVE = true;
        }
        cJSON_DeleteItemFromObject(json_item_CenterServer, "DataMasterServerIp");
        cJSON_DeleteItemFromObject(json_item_CenterServer, "DataMasterServerPort");

        // cJSON_DeleteItemFromObject(json_array, "QxUser");
        // cJSON_DeleteItemFromObject(json_array, "QxPassword");
        // cJSON_DeleteItemFromObject(json_array, "QxPort");
        // cJSON_DeleteItemFromObject(json_array, "QxServiceName");
        uint8_t *jsonStr = cJSON_Print(root);
        websWrite(wp, "%s", jsonStr);
        cJSON_Delete(root);
        free(jsonStr);
    }
    else
    {
        websSetStatus(wp, HTTP_CODE_INTERNAL_SERVER_ERROR);
    }

    websFlush(wp, 0);
    websDone(wp);
}

static void modifyJson(Webs *wp)
{
    if (IS_SAVE == false)
    {
        websSetStatus(wp, HTTP_CODE_INTERNAL_SERVER_ERROR);
    }
    else if (scaselessmatch(wp->method, "POST"))
    {
        const uint8_t *postData = wp->input.buf;
        cJSON *root = cJSON_Parse(postData);
        if (root == NULL)
        {
            websSetStatus(wp, HTTP_CODE_INTERNAL_SERVER_ERROR);
        }
        else
        {
            cJSON *json_item_CenterServer = cJSON_GetObjectItem(root, "CenterServer");
            cJSON *json_laneroad = cJSON_GetObjectItem(root, "LaneRoad");
            cJSON *json_array = cJSON_GetArrayItem(json_laneroad, 0);

            if (json_item_CenterServer != NULL && json_array != NULL)
            {
                cJSON_AddStringToObject(json_item_CenterServer, "DataMasterServerIp", REMOTE_SERVER_IP);
                cJSON_AddNumberToObject(json_item_CenterServer, "DataMasterServerPort", REMOTE_SERVER_PORT);
                cJSON_ReplaceItemInObject(root, "CenterServer", json_item_CenterServer);

                // cJSON_AddStringToObject(json_array, "QxUser", qx_user);
                // cJSON_AddStringToObject(json_array, "QxPassword", qx_password);
                // cJSON_AddStringToObject(json_array, "QxServiceName", qx_service_name);
                // cJSON_AddNumberToObject(json_array, "QxPort", qx_port);
                cJSON_ReplaceItemInArray(json_laneroad, 0, json_array);
                cJSON_ReplaceItemInObject(root, "LaneRoad", json_laneroad);
            }
            else
            {
                websSetStatus(wp, HTTP_CODE_INTERNAL_SERVER_ERROR);
            }

            FILE *f = fopen("/opt/Tim/remote_cfg.json", "w");
            if (f == NULL)
            {
                websSetStatus(wp, HTTP_CODE_INTERNAL_SERVER_ERROR);
            }
            else
            {
                uint8_t *postDataStr = cJSON_Print(root);
                fwrite(postDataStr, sizeof(uint8_t), strlen(postDataStr), f);
                fflush(f);
                fclose(f);
                websSetStatus(wp, HTTP_CODE_OK);
                free(postDataStr);
            }
            cJSON_Delete(json_item_CenterServer);
        }
        cJSON_Delete(root);
    }
    else
    {
        websSetStatus(wp, HTTP_CODE_INTERNAL_SERVER_ERROR);
    }

    writeCORSHeader(wp);
    websFlush(wp, 0);
    websDone(wp);
}

static void uploadFirmware(Webs *wp)
{
    WebsKey *s;
    WebsUpload *up;

    uint8_t *upfile;
    bool isSuccess = false;

    websSetStatus(wp, HTTP_CODE_OK);

    if (scaselessmatch(wp->method, "POST"))
    {
        for (s = hashFirst(wp->files); s; s = hashNext(wp->files, s))
        {
            up = s->content.value.symbol;

            uint8_t *upFileName = up->filename;
            uint8_t namePathBuf[1024] = {0};
            sprintf(namePathBuf, "%s/Tim.tar.gz", ME_GOAHEAD_UPLOAD_DIR);
            // upfile = sfmt("%s/%s", ME_GOAHEAD_UPLOAD_DIR, up->clientFilename);

            if (rename(upFileName, namePathBuf) < 0)
            {
                websSetStatus(wp, HTTP_CODE_INTERNAL_SERVER_ERROR);
                error("Cannot rename uploaded file: %s to %s, errno %d", up->filename, upfile, errno);
            }

            // wfree(upfile);

            // 开始升级
            //  解压缩
            uint8_t cmd[256] = {0};
            sprintf(cmd, "cd %s && gzip -dc Tim.tar.gz | tar -xvf -", ME_GOAHEAD_UPLOAD_DIR);
            system(cmd);
            // 1、保证后台跑起了Tim
            uint8_t retBuf[256] = {0};
            FILE *fp = popen("ps -ef |  grep -E ./Tim$ | awk '{print $2}' | awk 'NR==1{print}'", "r");
            fread(retBuf, 256, 1, fp);
            pclose(fp);
            uint32_t pid = atoi(retBuf);
            if (pid == 0)
            {
                // 没跑起
                websSetStatus(wp, HTTP_CODE_PARTIAL);
            }
            else
            {
                // 验证下固件能不能执行先
                mkdirCommon(ME_GOAHEAD_UPLOAD_DIR);
                memset(cmd, 0, sizeof(cmd));
                sprintf(cmd, "cd %s && chmod a+x Tim && ./Tim", ME_GOAHEAD_UPLOAD_DIR);
                int32_t systemRet = system(cmd);

                if (WEXITSTATUS(systemRet) == 104)
                {
                    logmsg(2, "新固件可运行，执行deploy.sh来完成基本的部署工作，并且10秒后重启");
                    isSuccess = true;
                }
                else
                {
                    error("新固件包Tim不可执行");
                    websSetStatus(wp, HTTP_CODE_COMMS_ERROR);
                };
            }
        }
    }
    writeCORSHeader(wp);
    websDone(wp);
    if (isSuccess == true)
    {
        uint8_t cmd[256] = {0};
        sprintf(cmd, "cd %s && mv Tim.tar.gz /opt/Tim ", ME_GOAHEAD_UPLOAD_DIR);
        system(cmd);

        memset(cmd, 0, sizeof(cmd));
        sprintf(cmd, "cd %s && rm -rvf `ls | grep -v Tim.tar.gz`", ME_GOAHEAD_UPLOAD_DIR);
        system(cmd);
        system("reboot");
    }
    else
    {
        uint8_t cmd[256] = {0};
        // rm -rvf `ls | grep -v 1` 除了1全删除
        sprintf(cmd, "cd %s && rm -rvf Tim.tar.gz", ME_GOAHEAD_UPLOAD_DIR);
        system(cmd);
    }
}

static void isTimLoaded(Webs *wp)
{
    websSetStatus(wp, HTTP_CODE_OK);

    uint8_t retBuf[256] = {0};
    FILE *fp = popen("ps -ef |  grep -E ./Tim$ | awk '{print $2}' | awk 'NR==1{print}'", "r");
    fread(retBuf, 256, 1, fp);
    pclose(fp);
    uint32_t pid = atoi(retBuf);
    if (pid == 0)
    {
        // 没跑起
        websSetStatus(wp, HTTP_CODE_INTERNAL_SERVER_ERROR);
    }
    writeCORSHeader(wp);
    websFlush(wp, 0);
    websDone(wp);
}

static void timLoaded(Webs *wp)
{
    system("cd /opt/Tim && sh start.sh");
    websSetStatus(wp, HTTP_CODE_OK);
    writeCORSHeader(wp);
    websFlush(wp, 0);
    websDone(wp);
}

int main(int argc, char **argv)
{
    // websSetDebug(1);
    // logSetPath("stdout:2");
    mkdirCommon(ME_GOAHEAD_UPLOAD_DIR);

    if (websOpen(HOME, ROUTE) < 0)
    {
        printf("Can't open the web server runtime");
        return 0;
    }

    websOptionsOpen();
    websDefineAction("getJson", getJson);
    websDefineAction("setJson", modifyJson);
    websDefineAction("isTimLoaded", isTimLoaded);
    websDefineAction("timLoaded", timLoaded);
    websDefineAction("uploadFirmware", uploadFirmware);

    if (websListen(":80") < 0)
    {
        printf("Can't listen on port 80");
        return 0;
    }

    websServiceEvents(NULL);
    websClose();
    return 0;
}